package com.younio.local.service;

import com.younio.local.compnent.LanLog;
import com.younio.local.repository.SynDerbyMysqlMapper;
import com.younio.local.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;

@Slf4j
@Service
@EnableScheduling
public class SynDerbyMysql {

    @Autowired
    private SynDerbyMysqlMapper synMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private Symbol symbol;

    private String mac;

    public static boolean started = false;

    private String[] tables = {"result_item", "proof_item", "meter_item", "test_item", "meter_type_item", "user_item", "version_item", "batch_item"};

    private boolean[] locks = {Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE};
    private boolean[] synFromZero = {Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE};

    private int[] index = new int[8];

    private boolean heartbeatRunning = false;
    private long times = 0L;

    @Scheduled(cron = "0/2 * * * * ?")
    public void heartbeat() {
        if (!heartbeatRunning) {
            heartbeatRunning = true;
            try {
                long start = System.currentTimeMillis();
                int mysqlHeartbeat = synMapper.mysqlHeartbeat();
                long e1 = System.currentTimeMillis();
                int derbyHeartbeat = synMapper.derbyHeartbeat();
                long e2 = System.currentTimeMillis();
                redisTemplate.opsForValue().set("test", "1");
                long e3 = System.currentTimeMillis();
                log.info("times:{}, mysql: {}-{}ms, derby:{}-{}ms, redis:{}-{}ms", times++, mysqlHeartbeat, e1 - start, derbyHeartbeat, e2 - e1, 1, e3 - e2);
                if (e3 - start > 3000) {
                    log.info("-----------------------------------------------------------------------------------------------------------------------");
                }
            } catch (Exception e) {
                log.info("###########################################################################################################");
                log.info("error:{}", e.getLocalizedMessage());
                log.info(e.toString());
            } finally {
                heartbeatRunning = false;
            }
        }


    }


    @Scheduled(cron = "0/5 * * * * ?")
    public void synResultItem() {
        doSyn(0);
    }

    @Scheduled(cron = "0/6 * * * * ?")
    public void synProofItem() {
        doSyn(1);
    }

    @Scheduled(cron = "0/10 * * * * ?")
    public void synMeterItem() {
        doSyn(2);
    }

    @Scheduled(cron = "0/8 * * * * ?")
    public void synTestItem() {
        doSyn(3);
    }

    @Scheduled(cron = "0/17 * * * * ?")
    public void synMeterTypeItem() {
        doSyn(4);
    }

    @Scheduled(cron = "0/53 * * * * ?")
    public void synUserItem() {
        doSyn(5);
    }

    @Scheduled(cron = "0/40 * * * * ?")
    public void synVersionItem() {
        doSyn(6);
    }


    @Scheduled(cron = "0/17 * * * * ?")
    public void synBatchItem() {
        doSyn(7);
    }


    private void doSyn(int idx) {
        if (locks[idx] && started) {
            locks[idx] = Boolean.FALSE;
            try {
                Integer maxDerbyIndex = Objects.defaultValue(synMapper.maxDerby(tables[idx]));
                Integer from = Objects.getOrDefault(synMapper.maxMysql(tables[idx], getMac()), synFromZero[idx] ? 0 : (index[idx] < maxDerbyIndex ? index[idx] : maxDerbyIndex));
                if (from < maxDerbyIndex) {
                    List<Map> mapList = synMapper.findMore(tables[idx], from);
                    if (mapList.size() > 0) {
                        synMapper.batchInsert(tables[idx], getMac(), mapList);
                        log.info("maxDerbyIndex = {}, from = {}, {}同步了{}记录", maxDerbyIndex, from, tables[idx], mapList.size());
                        LanLog.info("从" + tables[idx] + "同步了" + mapList.size() + "条数据");
                    }
                }
            } finally {
                locks[idx] = Boolean.TRUE;
            }
        }
    }

    public void init() {
        for (int i = 0; i < tables.length; i++) {
            setIndex(i);
            setSynFromZero(i);
        }
    }

    private synchronized void setSynFromZero(int idx) {
        int count = synMapper.count(tables[idx]);
        synFromZero[idx] = count < 1000;
        log.info("{}共有{}条记录，是否超过1000:{}", tables[idx], count, !synFromZero[idx]);
    }

    private synchronized void setIndex(int tableIndex) {
        index[tableIndex] = Objects.getOrDefault(synMapper.maxDerby(tables[tableIndex]), 0);
        log.info("{}的索引为{}", tables[tableIndex], index[tableIndex]);
    }


    private String getMac() {
        if (StringUtils.isEmpty(mac)) {
            mac = symbol.getPcSign();
        }
        return mac;
    }
}
